/**
 * Created on 2007-03-15 05:13
 */
package game.score;

import game.media.MediaManager;
import game.media.audio.Sound;
import internet.InternetReader;
import manager.Manager;

/**
 * @author David Wallin
 * 
 * Keeps track of the current score
 * The update(int deltaTime) method must be called every frame, with
 * an accurate deltaTime on this manager
 */
public class ScoreManager extends Manager
{
	/**
	 * The one instance of ScoreManager
	 */
	public static final ScoreManager		INSTANCE = new ScoreManager();
	/**
	 * The message shown if highscore can not be read
	 */
	public static final String				NO_HIGHSCORE_FOUND_STRING = "could not be determined";
	
	private final int						SCORE_TO_ADD_EVERY_SECOND = 500;
	private final int						LINES_TO_REMOVE_TO_GET_TO_NEXT_LEVEL = 3;
	
	private int								m_Score;
	private int								m_CurrentlyDisplayedScore;
	private int								m_CurrentLevel;
	private int								m_LinesRemovedThisLevel;
	private int								m_TotalLinesRemoved;
	private String							m_HighestScore;
	private Updater							m_Updater;
	
	private Sound							m_LevelUpSound;
	
	/**
	 * Register a number of removed lines to let the scoremanager calculate the score for this
	 * 
	 * @param linesToRegister the number of lines to register as removed
	 */
	public void registerRemovedLines(int linesToRegister)
	{
		m_Updater = new Updater(m_HighestScore);
		m_Updater.start();
		
		m_Score += ((m_CurrentLevel + 1) * getScoreForLines(linesToRegister));
		m_LinesRemovedThisLevel += linesToRegister;
		m_TotalLinesRemoved += linesToRegister;
		
		while(m_LinesRemovedThisLevel > LINES_TO_REMOVE_TO_GET_TO_NEXT_LEVEL)
		{
			m_LevelUpSound.play(false);
			m_LevelUpSound.play(true);
			m_CurrentLevel++;
			m_LinesRemovedThisLevel -= LINES_TO_REMOVE_TO_GET_TO_NEXT_LEVEL;
		}
	}
	
	/**
	 * Returns the score that's currently supposed to be shown. Call this to display
	 * the score in runtime, to get the effect of the score counting up really fast
	 * 
	 * @return the currently showing score
	 */
	public int getCurrentlyDisplayedScore()
	{
		return m_CurrentlyDisplayedScore;
	}
	
	/**
	 * Returns the real score at the moment. Call this when checking for highscore, or
	 * presenting the final score, at the end of a game
	 * 
	 * @return the current real score
	 */
	public int getRealScore()
	{
		return m_Score;
	}
	
	/**
	 * Returns the number corresponding to the current level
	 * 
	 * @return the current level
	 */
	public int getLevel()
	{
		return m_CurrentLevel;
	}
	/**
	 * Gets the total number of removed lines
	 * 
	 * @return number of removed lines
	 */
	public int getLines()
	{
		return m_TotalLinesRemoved;
	}
	
	/**
	 * Gets the highest score currently taken by any player
	 * 
	 * @return the highest score
	 */
	public String getHighestScore()
	{
		return m_HighestScore;
	}
	
	protected void doInitialize()
	{
		m_Score = 0;
		m_CurrentlyDisplayedScore = 0;
		m_CurrentLevel = 0;
		m_LinesRemovedThisLevel = 0;
		m_TotalLinesRemoved = 0;
		m_HighestScore = "";
		m_Updater = new Updater(NO_HIGHSCORE_FOUND_STRING);
		m_Updater.updateHighestScore();
		m_HighestScore = m_Updater.getInnerHighestScore();
		m_LevelUpSound = MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_LEVELUP);
	}
	
	/**
	 * Update routine
	 */
	protected void doUpdate(int deltaTime)
	{
		if(m_CurrentlyDisplayedScore < m_Score)
		{
			m_CurrentlyDisplayedScore += (int)((float)deltaTime / (float)1000 * (float)SCORE_TO_ADD_EVERY_SECOND);
			
			if(m_CurrentlyDisplayedScore > m_Score)
			{
				m_CurrentlyDisplayedScore = m_Score;
			}
		}
		
		//TODO find out if the next line is needed
		m_HighestScore = m_Updater.getInnerHighestScore();
	}
	
	/**
	 * Calculates the value of a specified number of lines. Teh current level
	 * is taken into account when doing this. If the asked number of lines
	 * would raise the current level will not have any effect on the outcome
	 * 
	 * @param lines the number of lines to check for value of
	 * @return the value of the asked number of lines
	 * @throws IllegalArgumentException if the number asked for is out of range
	 */
	private int getScoreForLines(int lines) throws IllegalArgumentException
	{
		int score = 0;
		
		if(lines == 1)
		{
			score = 100;
		}
		else if(lines == 2)
		{
			score = 500;
		}
		else if(lines == 3)
		{
			score = 1000;
		}
		else
		{
			throw new IllegalArgumentException("wierd!! the scoremanager was asked to convert an unsupported number " +
					"of removed lines into a score..\nValid values are 1, 2, and 3. Was: " + lines);
		}
		
		return score;
	}
	
	/**
	 * Constructor, does nothing. Private to ensure one instance only.
	 */
	private ScoreManager()
	{
	}
	
	/**
	 * @author David Wallin
	 * 
	 * The thread that updates the highest score. Should be run as separate thread every time
	 * the score is changed
	 */
	private class Updater extends Thread
	{
		private String		m_InnerHighestScore;
		
		public Updater(String lastHighestScore)
		{
			m_InnerHighestScore = lastHighestScore;
		}
		
		public void run()
		{
			System.out.println("thread started..");
			updateHighestScore();
			System.out.println("thread ending..");
		}
		
		public void updateHighestScore()
		{
			try
			{
				InternetReader reader = new InternetReader(InternetReader.getHighscoreLocation());
				reader.loadDocument();
				m_InnerHighestScore = reader.readLine();
				System.out.println("updating highest score worked well");
			}
			catch(Exception e)
			{
				//reading highscore didn't work.. tough luck!! swallow exception
				//TODO implement "fail n times == no more checks"
				m_InnerHighestScore = NO_HIGHSCORE_FOUND_STRING;
				System.out.println("updating highest failed");
			}
		}
		
		public String getInnerHighestScore()
		{
			return m_InnerHighestScore;
		}
	}
}
